//===--- Flatten.swift.gyb ------------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

/// An iterator that produces the elements contained in each segment
/// produced by some `Base` Iterator.
///
/// The elements traversed are the concatenation of those in each
/// segment produced by the base iterator.
///
/// - Note: This is the `IteratorProtocol` used by `FlattenSequence`,
///   `FlattenCollection`, and `BidirectionalFlattenCollection`.
public struct FlattenIterator<
  Base : IteratorProtocol where Base.Element : Sequence
> : IteratorProtocol, Sequence {

  /// Construct around a `base` iterator.
  internal init(_ base: Base) {
    self._base = base
  }

  /// Advance to the next element and return it, or `nil` if no next
  /// element exists.
  ///
  /// - Precondition: `next()` has not been applied to a copy of `self`
  ///   since the copy was made, and no preceding call to `self.next()`
  ///   has returned `nil`.
  public mutating func next() -> Base.Element.Iterator.Element? {
    repeat {
      if _fastPath(_inner != nil) {
        let ret = _inner!.next()
        if _fastPath(ret != nil) {
          return ret
        }
      }
      let s = _base.next()
      if _slowPath(s == nil) {
        return nil
      }
      _inner = s!.makeIterator()
    }
    while true
  }

  internal var _base: Base
  internal var _inner: Base.Element.Iterator?
}

/// A sequence consisting of all the elements contained in each segment
/// contained in some `Base` sequence.
///
/// The elements of this view are a concatenation of the elements of
/// each sequence in the base.
///
/// The `flatten` property is always lazy, but does not implicitly
/// confer laziness on algorithms applied to its result.  In other
/// words, for ordinary sequences `s`:
///
/// * `s.flatten()` does not create new storage
/// * `s.flatten().map(f)` maps eagerly and returns a new array
/// * `s.lazy.flatten().map(f)` maps lazily and returns a `LazyMapSequence`
///
/// - See also: `FlattenCollection`
public struct FlattenSequence<
  Base : Sequence where Base.Iterator.Element : Sequence
> : Sequence {

  /// Creates a concatenation of the elements of the elements of `base`.
  ///
  /// - Complexity: O(1)
  internal init(_ base: Base) {
    self._base = base
  }

  /// Returns an iterator over the elements of this sequence.
  ///
  /// - Complexity: O(1).
  public func makeIterator() -> FlattenIterator<Base.Iterator> {
    return FlattenIterator(_base.makeIterator())
  }
  
  internal var _base: Base
}

extension Sequence where Iterator.Element : Sequence {
  /// A concatenation of the elements of `self`.
  @warn_unused_result
  public func flatten() -> FlattenSequence<Self> {
    return FlattenSequence(self)
  }
}

extension LazySequenceProtocol
  where
  Elements.Iterator.Element == Iterator.Element,
  Iterator.Element : Sequence {

  /// A concatenation of the elements of `self`.
  @warn_unused_result
  public func flatten() -> LazySequence<
    FlattenSequence<Elements>
  > {
    return FlattenSequence(elements).lazy
  }
}

% for traversal in ('Forward', 'Bidirectional'):
%   t = '' if traversal == 'Forward' else traversal
%   Collection = 'Flatten%sCollection' % t
%   constraints = '%(Base)sIterator.Element : Collection'
%   if traversal == 'Bidirectional':
%     constraints += ''',
%   %(Base)sIndex : BidirectionalIndex, 
%   %(Base)sIterator.Element.Index : BidirectionalIndex'''
%   Index = Collection + 'Index'
/// A position in a `${Collection}`.
public struct ${Index}<
  BaseElements: Collection where ${constraints % {'Base': 'BaseElements.'}}
> : ${traversal}Index {
  /// Returns the next consecutive value after `self`.
  ///
  /// - Precondition: The next value is representable.
  public func successor() -> ${Index} {
    let nextInner = _inner!.successor()
    if _fastPath(nextInner != _innerCollection!.endIndex) {
      return ${Index}(_base, _outer, nextInner, _innerCollection)
    }
    for nextOuter in _outer.successor()..<_base.endIndex {
      let nextInnerCollection = _base[nextOuter]
      if !nextInnerCollection.isEmpty {
        return ${Index}(
          _base, nextOuter, nextInnerCollection.startIndex, nextInnerCollection)
      }
    }
    return ${Index}(_endIndexOfFlattened: _base)
  }
  
% if traversal == 'Bidirectional':
  /// Returns the previous consecutive value before `self`.
  ///
  /// - Precondition: The previous value is representable.
  public func predecessor() -> ${Index} {
    var prevOuter = _outer
    var prevInnerCollection : BaseElements.Iterator.Element
    if let ic = _innerCollection {
      prevInnerCollection = ic
    } else {
      prevOuter._predecessorInPlace()
      prevInnerCollection = _base[prevOuter]
    }
    var prevInner = _inner ?? prevInnerCollection.endIndex

    while prevInner == prevInnerCollection.startIndex {
      prevOuter._predecessorInPlace()
      prevInnerCollection = _base[prevOuter]
      prevInner = prevInnerCollection.endIndex
    }

    return ${Index}(
      _base, prevOuter, prevInner.predecessor(), prevInnerCollection)
  }
% end

  /// Construct the `startIndex` for a flattened view of `base`.
  internal init(_startIndexOfFlattened base: BaseElements) {
    for outer in base.indices {
      let innerCollection = base[outer]
      if !innerCollection.isEmpty {
        self._base = base
        self._outer = outer
        self._innerCollection = innerCollection
        self._inner = innerCollection.startIndex
        return
      }
    }
    self = ${Index}(_endIndexOfFlattened: base)
  }

  /// Construct the `endIndex` for a flattened view of `base`.
  internal init(_endIndexOfFlattened _base: BaseElements) {
    self._base = _base
    self._outer = _base.endIndex
    self._inner = nil
    self._innerCollection = nil
  }

  internal init(
    _ _base: BaseElements,
    _ outer: BaseElements.Index,
    _ inner: BaseElements.Iterator.Element.Index?,
    _ innerCollection: BaseElements.Iterator.Element?) {
    self._base = _base
    self._outer = outer
    self._inner = inner
    self._innerCollection = innerCollection
  }
  
  internal let _base: BaseElements
  internal let _outer: BaseElements.Index
  internal let _inner: BaseElements.Iterator.Element.Index?
  internal let _innerCollection: BaseElements.Iterator.Element?
}

@warn_unused_result
public func == <BaseElements> (
  lhs: ${Index}<BaseElements>, 
  rhs: ${Index}<BaseElements>
) -> Bool {
  return lhs._outer == rhs._outer && lhs._inner == rhs._inner
}

/// A flattened view of a base collection-of-collections.
///
/// The elements of this view are a concatenation of the elements of
/// each collection in the base.
///
/// The `flatten` property is always lazy, but does not implicitly
/// confer laziness on algorithms applied to its result.  In other
/// words, for ordinary collections `c`:
///
/// * `c.flatten()` does not create new storage
/// * `c.flatten().map(f)` maps eagerly and returns a new array
/// * `c.lazy.flatten().map(f)` maps lazily and returns a `LazyMapCollection`
///
/// - Note: The performance of accessing `startIndex`, `first`, any methods
///   that depend on `startIndex`, or of advancing a `${Collection}Index`
///   depends on how many empty subcollections are found in the base
///   collection, and may not offer the usual performance given by
///   `CollectionType` or `${traversal}IndexType`. Be aware, therefore, that
///   general operations on `${Collection}` instances may not have the
///   documented complexity.
///
/// - See also: `FlattenSequence`
public struct ${Collection}<
  Base: Collection where ${constraints % {'Base': 'Base.'}}
> : Collection {
  /// A type that represents a valid position in the collection.
  ///
  /// Valid indices consist of the position of every element and a
  /// "past the end" position that's not valid for use as a subscript.
  public typealias Index = ${Index}<Base>
  
  /// Creates a flattened view of `base`.
  public init(_ base: Base) {
    self._base = base
  }

  /// Returns an iterator over the elements of this sequence.
  ///
  /// - Complexity: O(1).
  public func makeIterator() -> FlattenIterator<Base.Iterator> {
    return FlattenIterator(_base.makeIterator())
  }

  /// The position of the first element in a non-empty collection.
  ///
  /// In an empty collection, `startIndex == endIndex`.
  public var startIndex: Index {
    return ${Index}(_startIndexOfFlattened: _base)
  }

  /// The collection's "past the end" position.
  ///
  /// `endIndex` is not a valid argument to `subscript`, and is always
  /// reachable from `startIndex` by zero or more applications of
  /// `successor()`.
  public var endIndex: Index {
    return ${Index}(_endIndexOfFlattened: _base)
  }

  /// Access the element at `position`.
  ///
  /// - Precondition: `position` is a valid position in `self` and
  ///   `position != endIndex`.
  public subscript(
    position: Index
  ) -> Base.Iterator.Element.Iterator.Element {
    return position._innerCollection![position._inner!]
  }

  // To return any estimate of the number of elements, we have to start
  // evaluating the collections.  That is a bad default for `flatMap()`, so
  // just return zero.
  public var underestimatedCount: Int { return 0 }

  public func _copyToNativeArrayBuffer()
    -> _ContiguousArrayBuffer<Base.Iterator.Element.Iterator.Element> {

    // The default implementation of `_copyToNativeArrayBuffer` queries the
    // `count` property, which materializes every inner collection.  This is a
    // bad default for `flatMap()`.  So we treat `self` as a sequence and only
    // rely on underestimated count.
    return _copySequenceToNativeArrayBuffer(self)
  }

  internal var _base: Base
}

extension Collection where ${constraints % {'Base': ''}} {
  /// A concatenation of the elements of `self`.
  @warn_unused_result
  public func flatten() -> ${Collection}<Self> {
    return ${Collection}(self)
  }
}

extension LazyCollectionProtocol
  where ${constraints % {'Base': ''}}, 
  ${constraints % {'Base': 'Elements.'}},
  Iterator.Element == Elements.Iterator.Element {
  /// A concatenation of the elements of `self`.
  @warn_unused_result
  public func flatten() -> LazyCollection<${Collection}<Elements>> {
    return ${Collection}(elements).lazy
  }
}

% end

@available(*, unavailable, renamed: "FlattenIterator")
public struct FlattenGenerator<
  Base : IteratorProtocol where Base.Element : Sequence
> {}

extension FlattenSequence {
  @available(*, unavailable, renamed: "makeIterator")
  public func generate() -> FlattenIterator<Base.Iterator> {
    fatalError("unavailable function can't be called")
  }
}

% for traversal in ('Forward', 'Bidirectional'):
%   t = '' if traversal == 'Forward' else traversal
%   Collection = 'Flatten%sCollection' % t
extension ${Collection} {
  @available(*, unavailable, message: "Please use underestimatedCount property instead.")
  public func underestimateCount() -> Int {
    fatalError("unavailable function can't be called")
  }
}
%end
